﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Xml;
using UnityEngine;
using Warensoft.Unity.Communication.Client;
using Warensoft.Unity.Communication.Client.Models;


namespace Warensoft.Unity.Communication.Client.DataClient
{
    public class DataContext
    {
        private string url;
        public string ServiceUrl
        {
            get
            {
                return this.url;
            }
        }
        private Socket socketClient;
        SocketAsyncEventArgs connectArgs = new SocketAsyncEventArgs();
        SocketAsyncEventArgs sendArgs = new SocketAsyncEventArgs();
        SocketAsyncEventArgs receiveArgs = new SocketAsyncEventArgs();
        public event EventHandler<ErrorEventArgs> Error;
        private Queue packageQueue=new Queue ();

        internal Queue PackageQueue
        {
            get { return packageQueue; }
            set { packageQueue = value; }
        }

        public bool SchemaLoaded { get; set; }
        public event EventHandler SchemaLoadCompleted;
        private Dictionary<string, Action<DataServicePackage>> processors = new Dictionary<string, Action<DataServicePackage>>();
        internal DataSet dsSchema = new DataSet();
        private Dictionary<Guid, Action<DataServicePackage>> callbackList = new Dictionary<Guid, Action<DataServicePackage>>();
        private Dictionary<string, DataEntitySet> tables;

        public Dictionary<string, DataEntitySet> Tables
        {
            get { return tables; }
            set { tables = value; }
        }

        internal DataContext(string url)
        {
            this.url = url;
            DataServicePackage package = new DataServicePackage();
            package.Command = "GetSchema";
            httpClient = HttpClient.CreateInstance();
            this.httpClient.Error += httpClient_Error;
            this.SendMessage(package, (response) =>
            {
                StringReader sr = new StringReader(response.Result.AdditionalData.ToString());
                this.dsSchema.ReadXml(sr, XmlReadMode.ReadSchema);
                this.tables = new Dictionary<string, DataEntitySet>();
                foreach (DataTable table in this.dsSchema.Tables)
                {
                    this.tables.Add(table.TableName, new DataEntitySet(this, table));
                }
                if (this.SchemaLoadCompleted != null)
                {
                    this.SchemaLoadCompleted(this, new EventArgs());
                }
                this.SchemaLoaded = true;

            });
        }

        void httpClient_Error(object sender, HttpRequestErrorEventArgs e)
        {
            this.RaiseError(string.Format("http请求发生异常，状态编号：{0},\r\n{1}", e.Status, e.ResponseText));
        }
       
        public void SendMessage(DataServicePackage package)
        {
            this.SendMessage(package,null);
        }

        public void SendMessage(DataServicePackage package, Action<DataServicePackage> callback)
        {
            Guid id = Guid.NewGuid();
            this.SendMessage(id, package, callback);

        }
        public void SendMessage(Guid packageID, DataServicePackage package, Action<DataServicePackage> callback)
        {
            package.ID = packageID;
            httpClient.BeginPost(this.url, package, (response) => 
            {
                if (callback!=null)
                {
                    var data = response.GetData<DataServicePackage>();
                    callback(data); 
                }
            });
            
        }
        internal bool locked = false;
        private HttpClient httpClient;
        public void SaveChanges()
        {
            this.SaveChanges(RefreshMode.StoreWins);
        }
        public void SaveChanges(RefreshMode refreshMode)
        {
            this.locked = true;
            Guid packageID = Guid.NewGuid();
            List<DataEntity> entities = new List<DataEntity>();
            foreach (var set in this.Tables)
            {
                entities.AddRange(set.Value.ChangedEntities);
            }
            foreach (var entity in entities)
            {
                entity.UpdateStub = packageID;
            }
            DataServicePackage package = new DataServicePackage();
            package.Command = "BatchUpdate";
            package.RefreshMode = refreshMode;
            package.Result = new ResultInfo()
            {
                AdditionalData = entities.ToByteArray()
            };

            this.SendMessage(packageID, package, (result) =>
            {
                if (result.Result.OperationDone)
                {
                    foreach (var table in this.tables)
                    {
                        table.Value.AcceptChanges(result.ID);
                    }
                }
                this.locked = false;
            });
        }
        protected void RaiseError(string message)
        {
            if (this.Error !=null )
            {
                this.Error(this, new ErrorEventArgs() 
                {
                    Message =message
                });
            }
        }
    }
}
